# ICEMGenerator - 高效的ICEM CFD网格划分Replay Script生成工具
# 模块： Block类，用于创建Block，并通过对Block的划分得到六面体或棱柱形网格
# 作者： weixc
# 邮箱： 12028045 [at] zju.edu.cn
# 项目主页： https://gitee.com/weixinchong/icemgenerator

from collections.abc import Iterable
from .param_check import NoBlockError

class Block():
    '''所有Block的超类'''

    members = []  #现有块的成员列表
    dimension = 3  #网格的维度
    
    def __str__(self):
        return self.name

    def __repr__(self):
        return self.__str__()

    def setName(self, name: str):
        '''设置Block的名称
        :param name: 要设定的Block名称'''
        self.name = name

    def set_replay(self, script: str):
        '''手动添加构建Block的Replay Script
        :param script: 手动添加的Replay Script'''
        self.method = 'User Defined'
        self.script = script

    def members_replay() -> str:
        '''生成所有块的Replay Script
        :return: 生成的Replay Script'''
        text = ''
        for b in Block.members:
            text += '\n' + b.replay_create()  #创建Block
        return text

    def replay_create(self) -> str:
        '''生成创建Block的Replay Script
        :return: 生成的Replay Script'''
        if self.method is None:
            raise NoBlockError('未构建Block，无法生成创建Block的Replay Script')
        return self.script


class Block2D(Block):
    '''创建一个Block'''

    def __init__(self, parts_inside: Iterable, parts_boundary: Iterable, name: str):
        '''创建基于指定Part构成的二维Block
        :param parts_inside: 块内部的Part或Body
        :param parts_boundary: 围成块边界的Part
        :param name: 块的名称'''
        self.parts = parts_inside + parts_boundary
        self.surfaces_for_block = []
        for pt in parts_inside:  #对于二维模型，内部区域是由二维的面组合而成的
            if 'Body' in str(type(pt)):
                self.surfaces_for_block += pt.surfaces
            else:
                self.surfaces_for_block += pt.objects
        self.name = name
        Block.dimension = 2
        Block.members.append(self)  #将当前块增加到成员列表

    def use_surface_blocking(self) -> str:
        '''采用2D Surface Blocking方法，生成创建Block的Replay Script'''
        surface_objects_expr = ' '.join([str(oi) for oi in self.surfaces_for_block])
        name = self.name
        self.method = '2D Surface Blocking'
        # 创建Block
        self.script = f'ic_geo_new_family {name}\n' \
             + f'ic_hex_surface_blocking {name} -mapped -min_edge 0.0 -surfaces {surface_objects_expr} -show_progress\n' \
             + 'ic_hex_unblank_blocks\n' \
             + 'ic_hex_set_unstruct_face_method auto\n' \
             + 'ic_hex_multi_grid_level 0\n' \
             + 'ic_hex_projection_limit 0\n' \
             + 'ic_hex_default_bunching_law default 2.0\n' \
             + 'ic_hex_floating_grid off\n' \
             + 'ic_hex_transfinite_degree 1\n' \
             + 'ic_hex_set_n_tetra_smoothing_steps 20\n' \
             + 'ic_hex_error_messages off_minor\n'
    
    
class Block3D(Block):
    '''创建一个Block'''

    def __init__(self, parts_inside: Iterable, parts_boundary: Iterable, name: str):
        '''创建基于指定Part构成的三维Block
        :param parts_inside: 块内部的Part或Body
        :param parts_boundary: 围成块边界的Part
        :param name: 块的名称'''
        self.parts = parts_inside + parts_boundary
        self.surfaces_for_block = []
        for pt in parts_boundary:  #对于三维模型，边界是由二维的面围成的
            self.surfaces_for_block += pt.objects
        self.name = name
        Block.dimension = 3
        self.method = None
        self.script = ''
        Block.members.append(self)  #将当前块增加到成员列表

    def use_3d_multizone(self, swept_mesh_type = 'all_quads', free_face_mesh_type = 'auto'):
        '''采用3D Multizone方法，生成创建Block的Replay Script
        :param swept_mesh_type: Swept Mesh Type，可选值有several_tris(hexa/prism)、all_tris(prism)和all_quads(hexa)
        :param free_face_mesh_type: Free Face Mesh Type，可选值有auto(Default)、uniform_quad(Uniform)和gambit_pave(Pave)'''
        surface_objects_expr = ' '.join([str(oi) for oi in self.surfaces_for_block])
        name = self.name
        self.method = '3D Multizone'
        # 创建Block
        self.script = f'ic_geo_new_family {name}\n' \
            + f'ic_hex_multizone_blocking -inherited -swept -min_edge 0.0 -automatic_sweep -uface_type {swept_mesh_type} -swept_surfaces {surface_objects_expr} -save_2d_blocking -show_progress -volfam {name}\n' \
            + 'ic_hex_unblank_blocks\n' \
            + f'ic_hex_set_unstruct_face_method {free_face_mesh_type}\n' \
            + 'ic_hex_multi_grid_level 0\n' \
            + 'ic_hex_projection_limit 0\n' \
            + 'ic_hex_default_bunching_law default 2.0\n' \
            + 'ic_hex_floating_grid off\n' \
            + 'ic_hex_transfinite_degree 1\n' \
            + 'ic_hex_set_n_tetra_smoothing_steps 20\n' \
            + 'ic_hex_error_messages off_minor\n'